/*
 * Indiana University Community Grid Computing Lab Software License,Version 1.1
 *
 * Copyright (c) 2002 Community Grid Computing Lab, Indiana University.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *      "This product includes software developed by the Indiana University
 *       Community Grid Computing Lab (http://www.communitygrids.iu.edu/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Indiana Univeristy","Indiana Univeristy Pervasive Techonology
 *    Labs" and  "Indiana Univeristy Community Grid Computing Lab" must not be
 *    used to endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    http://www.communitygrids.iu.edu/.
 *
 * 5. Products derived from this software may not use "Indiana
 *    University" name nor may "Indiana University" appear in their name,
 *    without prior written permission of the Indiana University.
 *
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * INDIANA UNIVERSITY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package cgl.sensorgrid.sopac.gps.filters;

/**
 * <p>Title: Sensor Grid</p>
 *
 * <p>Copyright: Copyright (c) 2005</p>
 *
 * <p>Company: CGL</p>
 *
 * @author Galip Aydin
 * @version 1.0
 */

import java.io.*;
import java.util.*;

import cgl.hpsearch.engine.URIBindings.*;
import cgl.narada.event.*;
import cgl.narada.matching.*;
import cgl.narada.service.*;
import cgl.narada.service.client.*;
import cgl.sensorgrid.sopac.gps.threadpool.*;

/**
 * This class shows an example worker thread that can
 * be used with the thread pool. It demonstrates the main
 * points that should be included in any worker thread. Use
 * this as a starting point for your own threads.
 *
 * @author Jeff Heaton (http://www.jeffheaton.com)
 * @version 1.0
 */
class CalcDisplacementFilter implements Runnable {
    public static double X0 = 0.0;
    public static double Y0 = 0.0;
    public static double Z0 = 0.0;

    public static double dX = 0.0;
    public static double dY = 0.0;
    public static double dZ = 0.0;

    static private int count = 0;
    private int taskNumber;
    protected cgl.sensorgrid.sopac.gps.threadpool.Done done;
    private NBEvent nbEvent;

    private volatile Thread checkThread; // = Thread.currentThread();
    int[] a = new int[1];

    private NBNativeStreamHandler nbstream; // = new NBNativeStreamHandler(endpoint);
    private DataOutputStream dout; //= new DataOutputStream(nbstream.getOutputStream());

    public CalcDisplacementFilter(NBNativeStreamHandler nbstream,
                                  NBEvent nbEvent) {
        try {
            count++;
            taskNumber = count;
            this.nbstream = nbstream;
            this.nbEvent = nbEvent;
            a[0] = 1;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     *
     * @param done
     */
    CalcDisplacementFilter(NBEvent _nbEvent) {
        nbEvent = _nbEvent;
        count++;
        taskNumber = count;
    }

    public void run() {
        byte[] data = nbEvent.getContentPayload();
        if (data != null && data.length > 0) {
            extractStation(data);
            count--;
        }
    }

    public void extractStation(byte[] data) {
        try {
            String msg = new String(data);
            //System.out.println(msg);
            Calendar cal = new GregorianCalendar();
//            System.out.print(cal.get(Calendar.HOUR_OF_DAY) +
//                                   ":" +
//                                   cal.get(Calendar.MINUTE) + ":" +
//                                   cal.get(Calendar.SECOND) + "\t");

            double lat = 0.0;
            double lon = 0.0;
            String time = "";

            StringTokenizer st = new StringTokenizer(msg, "\n");
            while (st.hasMoreTokens()) {
                String token = st.nextToken();
                if (!token.trim().equalsIgnoreCase("EOF")) {
                    StringTokenizer st1 = new StringTokenizer(token, "\t");
                    int cnt = 0;
                    while (st1.hasMoreTokens()) {
//                        cal = new GregorianCalendar();
                        String token1 = st1.nextToken();
                        if (cnt == 2) {
                            time = token1;
//                            System.out.print(token1 + "\t");
                        }

                        if (cnt == 3) { //X
                            double X1 = Double.parseDouble(token1);
                            if (X0 == 0.0) {
                                X0 = X1;
                            } else {
                                dX = X1 - X0;
                            }
                            X0 = X1;
                        }

                        else if (cnt == 4) { //Y
                            double Y1 = Double.parseDouble(token1);
                            if (Y0 == 0.0) {
                                Y0 = Y1;
                            } else {
                                dY = Y1 - Y0;
                            }
                            Y0 = Y1;
                        }

                        else if (cnt == 5) { //Z
                            double Z1 = Double.parseDouble(token1);
                            if (Z0 == 0.0) {
                                Z0 = Z1;
                            } else {
                                dZ = Z1 - Z0;
                            }
                            Z0 = Z1;
                        } else if (cnt == 7) { //Lat
                            lat = Double.parseDouble(token1);
                        } else if (cnt == 8) { //Lon
                            lon = Double.parseDouble(token1);
                        }
                        cnt++;
                    }
                    //System.out.print(time + "\t");
//                    System.out.println(dX + "\t" + dY + "\t" + dZ + "\t" + lat + "\t" + lon);
                    vxyz2vneu(lat, lon, dX, dY, dZ);
                }
//                nbstream.write((token + "\nEOF\n").getBytes());
//                    nbstream.flush();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }


    public void vxyz2vneu(double lat, double lon, double dX, double dY,
                          double dZ) {
        /* from tr_coord.pm
           #------------------------------------------------------ -
           #vxyz2vneu(latitude, longitude, VX, VY, VZ) converts
           #XYZ velocities or sigma 'sinto spherical
           #North(m), East(m) and Vertical(m)
           #by mvandomselaar @ucsd.edu(mod pfang for efficiency and clarity)
           #------------------------------------------------------ -
         */
//            my($lat, $lon, $dX, $dY, $dZ) = @_;
//
        double r2d = Math.atan2(1, 1) / 45.;
        double sinlat = Math.sin(lat * r2d);
        double sinlon = Math.sin(lon * r2d);
        double coslat = Math.cos(lat * r2d);
        double coslon = Math.cos(lon * r2d);
//
        double dN = -dX
                    * sinlat * coslon - dY
                    * sinlat
                    * sinlon + dZ
                    * coslat;
        double dE = -dX
                    * sinlon + dY
                    * coslon;
        double dU = dX
                    * coslat * coslon + dY
                    * coslat
                    * sinlon + dZ
                    * sinlat;

        //System.out.println(dN + "\t" + dE + "\t" + dU);
//            //return ($dN, $dE, $dU);
    }


}


public class CalcDisplacement implements NBEventListener {

    private static ThreadPool pool = new ThreadPool(10);
    private String hostName;
    private String portNum;
    private String ryoTopic;
    private ClientService clientService;
    private EventConsumer consumer;
    private Profile profile;
    private String stationName;

    private NBNativeStreamHandler nbStream;

    public CalcDisplacement(String hostName,
                            String portNum,
                            String ryoTopic,
                            String asciiTopic,
                            String stationName) {
        try {
            this.stationName = stationName;
            this.hostName = hostName;
            this.portNum = portNum;
            this.ryoTopic = ryoTopic;
            String endpoint = "niotcp://" + hostName + ":" + portNum +
                              asciiTopic;
            //System.out.println("DecodeRYO endpoint= " + endpoint);
            this.nbStream = new NBNativeStreamHandler(endpoint);

            clientService = SessionService.getClientService((int) (System.
                    currentTimeMillis()));
        } catch (ServiceException ex) {
            ex.printStackTrace();
        }
    }


    public void start() {
        try {
            initializeSubscriber();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public void stopConnection() {
        try {
            this.stopConnection();
            this.closeBrokerConnection();
        } catch (ServiceException ex) {
            ex.printStackTrace();
        }
    }

    public void initializeSubscriber() {
        Properties props = new Properties();
        /** These properties pertain to setting up a TCP link */
        props.put("hostname", this.hostName);
        props.put("portnum", this.portNum);
        try {
            initializeBrokerCommunications(props, "niotcp");
            initializeConsumer(this.ryoTopic);
        } catch (ServiceException serEx) {
            System.out.println(serEx);
        }
    }

    public void initializeBrokerCommunications(Properties props,
                                               String commType) throws
            ServiceException {
        clientService.initializeBrokerCommunications(props, commType);
    }

    public void initializeConsumer(String topic) throws ServiceException {
        profile =
                clientService.createProfile(TemplateProfileAndSynopsisTypes.
                                            STRING,
                                            topic);
        consumer = clientService.createEventConsumer(this);
        consumer.subscribeTo(profile);
    }

    /**
     *
     * @param nbEvent NBEvent
     */
    public void onEvent(NBEvent nbEvent) {
        if (nbEvent.getContentPayload() != null) {
            if (nbEvent != null) {
                //System.out.println(new String(nbEvent.getContentPayload()));
                pool.assign(new CalcDisplacementFilter(nbStream, nbEvent));
            }
        }
    }

    public void closeBrokerConnection() throws ServiceException {
        clientService.closeBrokerConnection();
        clientService.terminateServices();
    }

    /**
     * Main entry point.
     *
     * @param args  No arguments are used.
     */
    public static void main(String args[]) {
        CalcDisplacement test = new CalcDisplacement("localhost",
                "3045",
                "/SOPAC/GPS/SDCRTN/RAAP",
                "/SOPAC/GPS/SDCRTN/RAAP/Displacements", "RAAP");
        test.start();
    }
}
